package edu.ncsu.contractminer.event;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import edu.ncsu.contractminer.utilities.Constants;
import edu.stanford.nlp.ie.AbstractSequenceClassifier;
import edu.stanford.nlp.ie.crf.CRFClassifier;
import edu.stanford.nlp.ling.CoreLabel;
import edu.stanford.nlp.objectbank.TokenizerFactory;
import edu.stanford.nlp.parser.lexparser.LexicalizedParser;
import edu.stanford.nlp.process.CoreLabelTokenFactory;
import edu.stanford.nlp.process.PTBTokenizer;
import edu.stanford.nlp.trees.GrammaticalStructure;
import edu.stanford.nlp.trees.GrammaticalStructureFactory;
import edu.stanford.nlp.trees.LabeledScoredTreeFactory;
import edu.stanford.nlp.trees.ParentalTreeWrapper;
import edu.stanford.nlp.trees.PennTreebankLanguagePack;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.TreeFactory;
import edu.stanford.nlp.trees.TreePrint;
import edu.stanford.nlp.trees.TreebankLanguagePack;
import edu.stanford.nlp.trees.TypedDependency;
import edu.stanford.nlp.util.Filter;
import edu.stanford.nlp.util.Triple;

public class EventClassifierFeatureExtractor {

	private static Set<String> triggerWords = new HashSet<String>();
	private static Set<String> namedEntitiesFromDictionary = new HashSet<String>();

	static {
		triggerWords.add("agree");
		triggerWords.add("agrees");
		triggerWords.add("warrant");
		triggerWords.add("warrants");

		// prenoun should be included
		namedEntitiesFromDictionary.add("motorola");
		namedEntitiesFromDictionary.add("ase");
		namedEntitiesFromDictionary.add("party");
		namedEntitiesFromDictionary.add("parties");
		namedEntitiesFromDictionary.add("client");
		namedEntitiesFromDictionary.add("you");

	}

	public static List<Tree> findTriggerSubTree(Tree t) {

		List<Tree> triggerSubTreeList = new LinkedList<Tree>();
		Set<Tree> subTrees = t.subTrees();
		for (Tree subTree : subTrees) {

			if (subTree.value().equals("VP") && subTree.firstChild() != null
					&& subTree.firstChild().value().equals("MD")) {

				triggerSubTreeList.add(subTree);
			} else if (subTree.value().equals("VP")
					&& subTree.firstChild() != null
					&& subTree.firstChild().firstChild() != null
					&& containsTriggerWords(subTree.firstChild().firstChild()
							.value()))
				triggerSubTreeList.add(subTree);
		}

		return triggerSubTreeList;
	}

	public static boolean containsTriggerWords(String phrase) {
		return triggerWords.contains(phrase.toLowerCase());
	}

	public static Tree pruneTree(Tree t) {

		TreeFactory tf = new LabeledScoredTreeFactory();
		Filter filter = new Filter<Tree>() {
			// @Override
			public boolean accept(Tree t) {
				boolean flag = !t.label().value().equals("SBAR")
						&& !t.label().value().equals("SBRQ")
						&& !t.label().value().equals("SINV")
						&& !t.label().value().equals("SQ")

						// && !t.label().value().equals("S")
						&& !t.label().value().equals("ADVP")
						// && !t.label().value().equals("PP")
						&& !t.label().value().equals("WHADJP")
						&& !t.label().value().equals("WHNP")
						&& !t.label().value().equals("WHVP")
						&& !t.label().value().equals("WHPP");
				return flag;
			}
		};
		Tree prunedTree = t.prune(filter, tf);
		return prunedTree;
	}

	public static String findSubject(Tree t) {
		String sub = "";

		// find preceding sibling NP as subject
		Tree parent = t.parent();
		if (parent != null) {
			Tree[] siblings = parent.children();
			for (Tree sibling : siblings) {
				boolean sameAsSelf = sibling.equals(t);
				boolean isNP = sibling.value().equals("NP");
				if (sameAsSelf)
					break;
				else if (isNP) {
					sub = sibling.getLeaves().toString();
					break;
				}
			}
			// can not find NP in preceding siblings, search for parent's
			// preceding sibling which is NP
			if (sub.equals("")) {
				Tree grandParent = parent.parent();
				if (grandParent != null) {
					Tree[] uncles = grandParent.children();
					for (Tree uncle : uncles) {
						boolean sameAsParent = uncle.equals(parent);
						boolean isNP = uncle.value().equals("NP");
						if (sameAsParent)
							break;
						else if (isNP) {
							sub = uncle.getLeaves().toString();
							break;
						}
					}
				}
			}
		}

		if (sub.length() > 1) {

			String tokens[] = sub.subSequence(1, sub.length() - 1).toString()
					.split(",");

			StringBuilder subStr = new StringBuilder();
			for (String token : tokens) {
				if (!token.trim().isEmpty())
					subStr.append(token.trim() + " ");
			}

			System.out.println("  SUBJECT " + sub);
			System.out.println("  SUBJECT " + subStr);
			return subStr.toString().trim();
		} else
			return "";
	}

	public static Set<String> recognizeOrganizationFromSentence(
			String sentence, AbstractSequenceClassifier orgClassifier) {
		Set<String> orgSet = new HashSet<String>();

		List<Triple<String, Integer, Integer>> list = orgClassifier
				.classifyToCharacterOffsets(sentence);

		for (int i = 0; i < list.size(); i++) {
			Triple<String, Integer, Integer> t = list.get(i);
			if (t.first().equals("ORGANIZATION")) {
				String org = sentence.subSequence(t.second, t.third).toString();
				orgSet.add(org.toLowerCase().trim());
			}
		}

		for(String org: orgSet)
		{
			System.out.println("  "+org);
		}
		return orgSet;
	}

	public static boolean subContainsOrg(String subject,
			Set<String> orgFromSentence) {
		if (!subject.isEmpty()) {

			// if the subject contains the NE words from a dictionary

			for (String org : orgFromSentence) {
				if (subject.toLowerCase().indexOf(org) != -1)
					return true;
			}

			String tokens[] = subject.split(" ");
			for (String token : tokens) {
				if (namedEntitiesFromDictionary.contains(token.trim()
						.toLowerCase()))
					return true;
			}
			
			return false;

			// simplistic approach to detect NE
			// int numberOfUpperCaseLetters = 0;
			// char subjectCharArray[] = subject.toCharArray();
			// for (char c : subjectCharArray) {
			// if (Character.isUpperCase(c)) {
			// numberOfUpperCaseLetters++;
			// }
			// }
			// if (numberOfUpperCaseLetters >= 2)
			// return true;
			// if the initials contains capitalizations.

		}
		return false;
	}

	public static String findClauseSignal(Tree t) {
		String clauseSignal = "";
		Tree parent = t.parent();
		if (parent != null) {
			Tree grandParent = parent.parent();
			if (grandParent != null) {
				if (grandParent.value().equals("SBAR")) {
					Tree oldestUncle = grandParent.firstChild();
					if (!oldestUncle.equals(parent)) {
						int size = oldestUncle.getLeaves().size();
						clauseSignal = oldestUncle.getLeaves().get(size - 1)
								.nodeString();
					}
				}
			}
		}
		return clauseSignal;
	}

	public static String findCounterSignal(Tree t) {
		String counterSignal = "";
		Tree parent = t.parent();
		if (parent != null) {
			Tree[] siblings = parent.children();
			{
				for (Tree sibling : siblings) {
					boolean sameAsSelf = sibling.equals(t);
					boolean isSBAR = sibling.value().equals("SBAR");
					if (!sameAsSelf && isSBAR) {
						int size = sibling.firstChild().getLeaves().size();
						counterSignal = sibling.firstChild().getLeaves().get(
								size - 1).nodeString();
					}
				}
			}
		}
		return counterSignal;
	}

	public static String findModalVerb(Tree t) {
		String modalVerb = "";
		if (t.firstChild() != null) {
			if (t.firstChild().value().equals("MD")) {
				if (t.firstChild().firstChild() != null)
					modalVerb = t.firstChild().firstChild().value();
			}
		}
		return modalVerb;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		LexicalizedParser lp = new LexicalizedParser(
				Constants.EnglishPCFGmodelFile);
		lp.setOptionFlags("-maxLength", "80", "-retainTmpSubcategories");

		AbstractSequenceClassifier orgClassifier = CRFClassifier
				.getClassifierNoExceptions(Constants.English3NERmodelFile);

		FileReader sentReader;
		try {

			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
			String dataSourceName = "NormDB";
			String dbURL = "jdbc:odbc:" + dataSourceName;
			Connection con = DriverManager.getConnection(dbURL, "", "");

			DatabaseMetaData dbm = con.getMetaData();
			// check if "employee" table is there
			ResultSet tables = dbm.getTables(null, null,
					Constants.eventTableName, null);
			if (tables.next()) {
				// Table exists
				Statement statement = con.createStatement();
				String cleanTable = "DELETE FROM " + Constants.eventTableName;
				Statement cleanStatement = con.createStatement();
				cleanStatement.executeUpdate(cleanTable);

			} else {
				Statement stmt = con.createStatement();

				// Create table called my_table
				String sql = "CREATE TABLE "
						+ Constants.eventTableName
						+ " (ID AUTOINCREMENT PRIMARY KEY, Sentence text,EventCandidate text, Subject text, ModalVerb text, ClauseSignal text, CounterClauseSignal text, IsEvent YESNO, SubContainsNE YESNO)";
				System.out.println(sql);
				stmt.executeUpdate(sql);
				// Table does not exist
			}

			// try and create a java.sql.Statement so we can run queries
			String sql = "INSERT INTO "
					+ Constants.eventTableName
					+ "(Sentence,EventCandidate,Subject,SubContainsNE,ModalVerb,ClauseSignal,CounterClauseSignal) VALUES(?,?,?,?,?,?,?)";
			PreparedStatement ps = con.prepareStatement(sql);

			sentReader = new FileReader(Constants.NormSentencesFile);
			BufferedReader bufferedSentReader = new BufferedReader(sentReader);
			String sentence;
			while ((sentence = bufferedSentReader.readLine()) != null) {

				Set<String> orgFromSentence = recognizeOrganizationFromSentence(
						sentence, orgClassifier);

				TokenizerFactory<CoreLabel> tokenizerFactory = PTBTokenizer
						.factory(new CoreLabelTokenFactory(), "");
				List<CoreLabel> rawWords = tokenizerFactory.getTokenizer(
						new StringReader(sentence)).tokenize();

				Tree tree = lp.apply(rawWords);

				ParentalTreeWrapper wrappedTree = new ParentalTreeWrapper(tree);
				// Tree wrappedTree = tree;
				// System.out.println("====orignal tree==");
				// wrappedTree.pennPrint();

				List<Tree> triggerList = findTriggerSubTree(wrappedTree);
				for (Tree triggerSubTree : triggerList) {

					// System.out.println("====triggered tree====");
					// triggerSubTree.pennPrint();

					// prune the subtree
					Tree prunedSubTree = pruneTree(triggerSubTree);
					// System.out.println("====pruned triggered tree====");
					// prunedSubTree.pennPrint();
					// Tree p = triggerSubTree.parent();
					// List<Tree> s = triggerSubTree.siblings(wrappedTree);

					String subject = findSubject(triggerSubTree);
					String clauseSignal = findClauseSignal(triggerSubTree);
					String counterSignal = findCounterSignal(triggerSubTree);
					String modalVerb = findModalVerb(triggerSubTree);

					System.out.println("SUBJECT OF THE CURRENT SUBTREE: "
							+ subject);
					System.out.println("?ContainNE: "
							+ subContainsOrg(subject, orgFromSentence));
					System.out.println("CLASUE SIGNAL OF THE CURRENT SUBTREE: "
							+ clauseSignal);
					System.out
							.println("COUNTER SIGNAL OF THE CURRENT SUBTREE: "
									+ counterSignal);
					System.out.println("MODAL VERB OF THE CURRENT SUBTREE: "
							+ modalVerb);
					System.out.println("");
					// Events(Sentence,EventCandidate,Subject,ModalVerb,ClauseSignal,CounterClauseSignal)
					ps.setString(1, sentence);
					// ps.setString(2, triggerSubTree.getLeaves().toString());
					ps.setString(2, prunedSubTree.getLeaves().toString());
					ps.setString(3, subject);
					ps.setBoolean(4, subContainsOrg(subject, orgFromSentence));
					ps.setString(5, modalVerb);
					ps.setString(6, clauseSignal);
					ps.setString(7, counterSignal);
					ps.executeUpdate();
				}

				// print dependencies
				// TreebankLanguagePack tlp = new PennTreebankLanguagePack();
				// GrammaticalStructureFactory gsf = tlp
				// .grammaticalStructureFactory();
				// GrammaticalStructure gs = gsf
				// .newGrammaticalStructure(wrappedTree);
				// List<TypedDependency> tdl =
				// gs.typedDependenciesCCprocessed();
				// System.out.println(tdl);
				// System.out.println();
				// print penn tree
				TreePrint tp = new TreePrint("penn");
				// tp.printTree(wrappedTree);

			}

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
